package japgolly.scalajs.react.facade

import org.scalajs.dom
import scala.annotation.nowarn
import scala.scalajs.js
import scala.scalajs.js.annotation.JSName

/** https://facebook.github.io/react/docs/events.html */
@js.native
trait SyntheticEvent[+DOMEventTarget <: dom.Node] extends js.Object {
  val bubbles         : Boolean        = js.native
  val cancelable      : Boolean        = js.native
  val currentTarget   : DOMEventTarget = js.native
  def defaultPrevented: Boolean        = js.native
  val eventPhase      : Double         = js.native
  val isTrusted       : Boolean        = js.native
  val nativeEvent     : dom.Event      = js.native
  val target          : DOMEventTarget = js.native
  val timeStamp       : Double         = js.native

  /**
   * Stops the default action of an element from happening.
   * For example: Prevent a submit button from submitting a form Prevent a link from following the URL
   */
  def preventDefault(): Unit = js.native

  /**
   * Stops the bubbling of an event to parent elements, preventing any parent event handlers from being executed.
   */
  def stopPropagation(): Unit = js.native

  def isPropagationStopped(): Boolean = js.native
  def isDefaultPrevented(): Boolean = js.native
  def isPersistent(): Boolean = js.native

  @JSName("type") val eventType: String = js.native

  /**
   * If you want to access the event properties in an asynchronous way, call this on the event, which will remove the
   * synthetic event from the pool and allow references to the event to be retained by user code.
   */
  def persist(): Unit = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticUIEvent.js */
@js.native
trait SyntheticUIEvent[+DOMEventTarget <: dom.Node] extends SyntheticEvent[DOMEventTarget] {
  override val nativeEvent: dom.UIEvent = js.native
  /**
   * The view attribute identifies the AbstractView from which the event was generated.
   * The un-initialized value of this attribute must be null.
   */
  val view: js.Object = js.native
  /**
   * Specifies some detail information about the Event, depending on the type of event.
   * The un-initialized value of this attribute must be 0.
   */
  def detail: Double = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticAnimationEvent.js */
@js.native
trait SyntheticAnimationEvent[+DOMEventTarget <: dom.Node] extends SyntheticEvent[DOMEventTarget] {
  val animationName: String
  val pseudoElement: String
  val elapsedTime: Double
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticTransitionEvent.js */
@js.native
trait SyntheticTransitionEvent[+DOMEventTarget <: dom.Node] extends SyntheticEvent[DOMEventTarget] {
  val propertyName: String
  val pseudoElement: String
  val elapsedTime: Double
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticClipboardEvent.js */
@js.native
trait SyntheticClipboardEvent[+DOMEventTarget <: dom.Node] extends SyntheticEvent[DOMEventTarget] {
  /**
   * The clipboardData attribute is an instance of the DataTransfer interface which lets a script read and manipulate
   * values on the system clipboard during user-initiated copy, cut and paste operations. The associated drag data store
   * is a live but filtered view of the system clipboard, exposing data types the implementation knows the script can
   * safely access.
   *
   * The clipboardData object's items and files properties enable processing of multi-part or non-textual data from the
   * clipboard.
   *
   * http://www.w3.org/TR/clipboard-apis/#widl-ClipboardEvent-clipboardData
   */
  def clipboardData: dom.DataTransfer = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticCompositionEvent.js */
@js.native
trait SyntheticCompositionEvent[+DOMEventTarget <: dom.Node] extends SyntheticEvent[DOMEventTarget] {
  override val nativeEvent: dom.CompositionEvent = js.native
  /**
   * Holds the value of the characters generated by an input method.
   * This may be a single Unicode character or a non-empty sequence of Unicode characters [Unicode].
   * Characters should be normalized as defined by the Unicode normalization form NFC, defined in [UAX #15].
   * This attribute may be null or contain the empty string.
   *
   * http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
   */
  val data: String = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticDragEvent.js */
@js.native
trait SyntheticDragEvent[+DOMEventTarget <: dom.Node] extends SyntheticMouseEvent[DOMEventTarget] {
  override val nativeEvent: dom.DragEvent = js.native
  val dataTransfer: dom.DataTransfer = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticFocusEvent.js */
@js.native
trait SyntheticFocusEvent[+DOMEventTarget <: dom.Node] extends SyntheticUIEvent[DOMEventTarget] {
  override val nativeEvent: dom.FocusEvent = js.native
  val relatedTarget: dom.EventTarget = js.native
}

@js.native
trait SyntheticFormEvent[+DOMEventTarget <: dom.Node] extends SyntheticUIEvent[DOMEventTarget]

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticKeyboardEvent.js */
@js.native
@nowarn("cat=unused")
trait SyntheticKeyboardEvent[+DOMEventTarget <: dom.Node] extends SyntheticUIEvent[DOMEventTarget] {
  override val nativeEvent: dom.KeyboardEvent = js.native
  val location : Double  = js.native
  val altKey   : Boolean = js.native
  val ctrlKey  : Boolean = js.native
  val metaKey  : Boolean = js.native
  val shiftKey : Boolean = js.native
  val repeat   : Boolean = js.native
  val locale   : String  = js.native
  def getModifierState(keyArg: String): Boolean = js.native

  /** The KeyboardEvent.code property represents a physical key on the keyboard (as opposed to the character generated by pressing the key). In other words, this property returns a value that isn't altered by keyboard layout or the state of the modifier keys.
    *
    * If the input device isn't a physical keyboard, but is instead a virtual keyboard or accessibility device, the returned value will be set by the browser to match as closely as possible to what would happen with a physical keyboard, to maximize compatibility between physical and virtual input devices.
    *
    * This property is useful when you want to handle keys based on their physical positions on the input device rather than the characters associated with those keys; this is especially common when writing code to handle input for games that simulate a gamepad-like environment using keys on the keyboard. Be aware, however, that you can't use the value reported by KeyboardEvent.code to determine the character generated by the keystroke, because the keycode's name may not match the actual character that's printed on the key or that's generated by the computer when the key is pressed.
    *
    * For example, the code returned is "KeyQ" for the Q key on a QWERTY layout keyboard, but the same code value also represents the ' key on Dvorak keyboards and the A key on AZERTY keyboards. That makes it impossible to use the value of code to determine what the name of the key is to users if they're not using an anticipated keyboard layout.
    *
    * To determine what character corresponds with the key event, use the KeyboardEvent.key property instead.
    */
  val code: String = js.native

  /** The KeyboardEvent interface's key read-only property returns the value of the key pressed by the user, taking into consideration the state of modifier keys such as Shift as well as the keyboard locale and layout. Its value is determined as follows:
    *
    *   - If the pressed key has a printed representation, the returned value is a non-empty Unicode character string containing the printable representation of the key.
    *   - If the pressed key is a control or special character, the returned value is one of the pre-defined key values.
    *   - If the KeyboardEvent represents the press of a dead key, the key value must be "Dead".
    *   - Some specialty keyboard keys (such as the extended keys for controlling media on multimedia keyboards) don't generate key codes on Windows; instead, they trigger WM_APPCOMMAND events. These events get mapped to DOM keyboard events, and are listed among the "Virtual key codes" for Windows, even though they aren't actually key codes.
    *   - If the key cannot be identified, the returned value is Unidentified.
    */
  val key: String = js.native

  /** printable keys (letters, numbers, symbols) */
  val charCode: Int = js.native

  /** non-printable keys (modifiers, arrow keys, tab, esc) */
  val keyCode: Int = js.native

  val which: Int = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticMouseEvent.js */
@js.native
@nowarn("cat=unused")
trait SyntheticMouseEvent[+DOMEventTarget <: dom.Node] extends SyntheticUIEvent[DOMEventTarget] {
  override val nativeEvent: dom.MouseEvent = js.native
  val screenX: Double = js.native
  val screenY: Double = js.native
  val clientX: Double = js.native
  val clientY: Double = js.native
  val pageX  : Double = js.native
  val pageY  : Double = js.native

  val altKey  : Boolean = js.native
  val ctrlKey : Boolean = js.native
  val metaKey : Boolean = js.native
  val shiftKey: Boolean = js.native

  /** 0 = left mouse button, 1 = middle, 2 = right */
  val button: Int = js.native

  val buttons: Int = js.native

  val relatedTarget: dom.EventTarget = js.native

  def getModifierState(keyArg: String): Boolean = js.native

  /** @since React 16.5 */ val movementX: Long = js.native
  /** @since React 16.5 */ val movementY: Long = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticPointerEvent.js */
@js.native
trait SyntheticPointerEvent[+DOMEventTarget <: dom.Node] extends SyntheticMouseEvent[DOMEventTarget] {
  override val nativeEvent: dom.PointerEvent = js.native
  val pointerId  : Double  = js.native
  val width      : Double  = js.native
  val height     : Double  = js.native
  val pressure   : Double  = js.native
  val tiltX      : Double  = js.native
  val tiltY      : Double  = js.native
  val pointerType: String  = js.native
  val isPrimary  : Boolean = js.native

  /** The normalized tangential pressure (also known as barrel pressure), typically set by an additional control
    * (e.g. a finger wheel on an airbrush stylus), of the pointer input in the range of [-1,1],
    * where 0 is the neutral position of the control.
    * Note that some hardware may only support positive values in the range of [0,1].
    * For hardware that does not support tangential pressure, the value MUST be 0.
    *
    * @since React 16.5
    */
  val tangentialPressure: Double = js.native

  /** The clockwise rotation (in degrees, in the range of [0,359])
    * of a transducer (e.g. pen stylus) around its own major axis.
    * For devices that do not report twist, the value MUST be 0.
    *
    * @since React 16.5
    */
  val twist: Int = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticTouchEvent.js */
@js.native
@nowarn("cat=unused")
trait SyntheticTouchEvent[+DOMEventTarget <: dom.Node] extends SyntheticUIEvent[DOMEventTarget] {
  override val nativeEvent: dom.TouchEvent = js.native
  val altKey        : Boolean       = js.native
  val ctrlKey       : Boolean       = js.native
  val metaKey       : Boolean       = js.native
  val shiftKey      : Boolean       = js.native
  val touches       : dom.TouchList = js.native
  val targetTouches : dom.TouchList = js.native
  val changedTouches: dom.TouchList = js.native
  def getModifierState(keyArg: String): Boolean = js.native
}

/** https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticWheelEvent.js */
@js.native
trait SyntheticWheelEvent[+DOMEventTarget <: dom.Node] extends SyntheticMouseEvent[DOMEventTarget] {
  override val nativeEvent: dom.WheelEvent = js.native
  val deltaX: Double = js.native
  val deltaY: Double = js.native
  val deltaZ: Double = js.native
  /**
   * Browsers without "deltaMode" is reporting in raw wheel delta where one
   * notch on the scroll is always +/- 120, roughly equivalent to pixels.
   * A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
   * ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
   */
  val deltaMode: Double = js.native
}
